package com.lobov.entities;

import java.io.Serializable;

import com.lobov.utils.Compressor;

/**
 * Лоскут матрицы - несколько строк или столбцов матрицы в зависимости от
 * процессоров в кластере (количество строк и столбцов в лоскуте варьируется при
 * создании лоскута в зависимости о количества процессоров и размера матрицы)
 * 
 * в каждом лоскуте как и в матрице свой сквозной порядковы номер элементов!!!
 */
public class Snip extends Timer implements EntityIface, Serializable {

	private static final long serialVersionUID = 1L;

	/**
	 * создать разреженный лоскут матрицы после резки
	 * 
	 * @param numOfElements
	 *            количество элементов в сжатом массиве
	 * @param numOfStartElement
	 *            с какого элемента начинается лоскут в материнской матрице
	 * @param xlength
	 *            ширина лоскута
	 * @param ylength
	 *            высота лоскута
	 * 
	 * @param orderNumInContainer
	 *            порядковый номер в контейнере
	 */
	public Snip(int numOfStartElement, int xlength, int ylength,
			int orderNumInContainer) {
		super();
		this.values = new double[xlength * ylength];
		this.xlength = xlength;
		this.ylength = ylength;
		this.numOfStartElement = numOfStartElement;
		this.compressed = false;
		this.orderNumInContainer = orderNumInContainer;
	}

	/**
	 * создать разреженный лоскут матрицы после умножения лоскута на матрицу
	 * 
	 * @param xlength
	 *            ширина лоскута
	 * @param ylength
	 *            высота лоскута
	 * @param orderNumInContainer
	 *            порядковый номер в контейнере
	 */
	public Snip(int xlength, int ylength, int orderNumInContainer) {
		super();
		this.values = new double[xlength * ylength];
		this.xlength = xlength;
		this.ylength = ylength;
		this.compressed = false;
		this.orderNumInContainer = orderNumInContainer;
	}

	/**
	 * создать сжатый лоскут матрицы
	 * 
	 * @param numOfElements
	 *            количество элементов в сжатом массиве
	 * @param numOfStartElement
	 *            номер первого элемента в лоскуте
	 * @param xlength
	 *            ширина лоскута
	 * @param ylength
	 *            высота лоскута
	 */
	public Snip(int numOfElements, int numOfStartElement, int xlength,
			int ylength, int orderNumInContainer) {
		super();
		this.values = new double[numOfElements];
		this.xs = new int[numOfElements];
		this.ys = new int[numOfElements];
		this.xlength = xlength;
		this.ylength = ylength;
		this.numOfStartElement = numOfStartElement;
		this.compressed = true;
		this.orderNumInContainer = orderNumInContainer;
	}

	private int numOfStartElement = -1; // после умножения на вектор не нужен
	private int xlength;
	private int ylength;
	private double[] values;
	private int[] xs;
	private int[] ys;
	private int orderNumInContainer = -1; // изначально номер не задан

	public boolean compressed;

	public int getXlength() {
		return xlength;
	}

	public void setXlength(int xlength) {
		this.xlength = xlength;
	}

	public int getYlength() {
		return ylength;
	}

	public void setYlength(int ylength) {
		this.ylength = ylength;
	}

	public double[] getValues() {
		return values;
	}

	public void setValues(double[] values) {
		this.values = values;
	}

	public int[] getXs() {
		return xs;
	}

	public void setXs(int[] xs) {
		this.xs = xs;
	}

	public int[] getYs() {
		return ys;
	}

	public void setYs(int[] ys) {
		this.ys = ys;
	}

	public int getNumOfStartElement() {
		return numOfStartElement;
	}

	public void setNumOfStartElement(int numOfStartElement) {
		this.numOfStartElement = numOfStartElement;
	}

	@Override
	public boolean isCompressed() {
		return compressed;
	}

	public void setCompressed(boolean isCompressed) {
		this.compressed = isCompressed;
	}

	public int getOrderNumInContainer() {
		return orderNumInContainer;
	}

	public void setOrderNumInContainer(int orderNumInContainer) {
		this.orderNumInContainer = orderNumInContainer;
	}

	@Override
	public String toString() {
		if (compressed) {
			return printCompressedToString();
		} else {
			return printUnCompressedToString();
		}
	}

	private String printUnCompressedToString() {
		StringBuilder out = new StringBuilder();
		if (xlength == 0 || ylength == 0) {
			return "Didn't define length by X and Y for matrix";
		}
		int num = 0;
		for (int y = 0; y < ylength; y++) {
			out.append("\n");
			for (int x = 0; x < xlength; x++) {
				out.append(values[num]);
				out.append(" ");
				num++;
			}
		}
		return out.toString();
	}

	private String printCompressedToString() {
		StringBuilder out = new StringBuilder();
		if (xlength == 0 || ylength == 0) {
			return "Didn't define length by X and Y for matrix";
		}
		int numCompress = 0; // порядок в сжатом формате
		int numValues = values.length;
		for (int y = 0; y < ylength; y++) {
			out.append("\n");
			for (int x = 0; x < xlength; x++) {
				if (numValues != 0 && xs[numCompress] == x
						&& ys[numCompress] == y) {
					out.append(values[numCompress]);
					numCompress++;
					numValues--;

				} else {
					out.append(0d);
				}
				out.append(" ");
			}
		}
		return out.toString();
	}

	@Override
	public double getElementByXY(int x, int y) throws Exception {
		if (x > xlength || y > ylength) {
			throw new Exception("В лоскуте нет элемента с X = " + x + " и Y = "
					+ y);
		}
		if (!compressed) {
			int n = 0;
			for (int h = 0; h < ylength; h++) {
				for (int l = 0; l < xlength; l++) {
					if (l == x - 1 && h == y - 1) {
						return values[n];
					}
					n++;
				}
			}
		} else {
			for (int n = 0; n < values.length; n++) {
				if (xs[n] == x - 1 && ys[n] == y - 1) {
					return values[n];
				}
			}
			return 0;
		}
		throw new Exception("В лоскуте нет элемента с X = " + x + " и Y = " + y);
	}

	@Override
	public EntityIface compress() throws Exception {
		return Compressor.compressSnip(this);
	}

	@Override
	public EntityIface deCompress() throws Exception {
		return Compressor.deCompressSnip(this);
	}

	@Override
	public double getElementByX(int x) throws Exception {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void setElementByX(int x, double value) throws Exception {
		// TODO Auto-generated method stub
	}

	@Override
	public void setElementByXY(int x, int y, double value) throws Exception {
		// TODO Auto-generated method stub
	}

}
